using Gj;
using Unity.Entities;
using Unity.Transforms;
using UnityEngine;

[UpdateInGroup(typeof(LateSimulationSystemGroup))]
[UpdateAfter(typeof(AnimationDataSystem))]
public partial class AnimationSystem : SystemBase
{
    private BeginSimulationEntityCommandBufferSystem CommandBufferSystem;
    private EntityQuery group;
    private EntityQuery rotationGroup;
    private EntityQuery scaleGroup;
    private EntityQuery translationGroup;

    protected override void OnCreate()
    {
        CommandBufferSystem = World.GetOrCreateSystemManaged<BeginSimulationEntityCommandBufferSystem>();
        group = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadWrite<AnimationData>()
            }
        });
        translationGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AnimationData>(),
                ComponentType.ReadOnly<AnimationTranslationData>(),
                ComponentType.ReadWrite<LocalTransform>()
            }
        });
        rotationGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AnimationData>(),
                ComponentType.ReadOnly<AnimationRotationData>(),
                ComponentType.ReadWrite<LocalTransform>()
            }
        });
        scaleGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new[]
            {
                ComponentType.ReadOnly<AnimationData>(),
                ComponentType.ReadOnly<AnimationScaleData>(),
                ComponentType.ReadWrite<LocalTransform>()
            }
        });
    }

    // OnUpdate runs on the main thread.
    protected override void OnUpdate()
    {
        var delay = SystemAPI.Time.DeltaTime;
        var inputDeps = Dependency;
        var commandBuffer = CommandBufferSystem.CreateCommandBuffer().AsParallelWriter();
        inputDeps = Entities.WithStoreEntityQueryInField(ref group).ForEach(
            (Entity entity, ref AnimationData animationData) =>
            {
                if (animationData.end) return;
                if (animationData.stop) return;
                animationData._time += delay;
                if (animationData.delay > 0 && animationData._time < animationData.delay) return;
                if (!animationData.start) animationData.start = true;
                if (animationData.value == (!animationData._invert ? animationData.to : animationData.from))
                    switch (animationData.loopType)
                    {
                        case LoopType.loop:
                            animationData._time = delay;
                            break;
                        case LoopType.pingPong:
                            animationData._time = delay;
                            animationData._invert = !animationData._invert;
                            break;
                        case LoopType.none:
                            animationData.end = true;
                            break;
                    }

                if (!animationData.end)
                {
                    if (animationData._time - animationData.delay >= animationData.time)
                    {
                        animationData.value = !animationData._invert ? animationData.to : animationData.from;
                    }
                    else
                    {
                        var t = (animationData._time - animationData.delay) / animationData.time;
                        animationData.value = MathTools.Compute(animationData.easeType,
                            animationData._invert ? animationData.to : animationData.from,
                            !animationData._invert ? animationData.to : animationData.from, t);
                    }
                }
            }).Schedule(inputDeps);

        inputDeps = Entities.WithStoreEntityQueryInField(ref translationGroup).ForEach(
            (Entity entity, AnimationData animationData, AnimationTranslationData animationTranslationData,
                ref LocalTransform translation) =>
            {
                if (animationData.end) return;
                if (animationData.stop) return;
                translation.Position = MathTools.Lerp(animationTranslationData.start, animationTranslationData.end,
                    animationData.value);
            }).Schedule(inputDeps);

        inputDeps = Entities.WithStoreEntityQueryInField(ref rotationGroup).ForEach(
            (Entity entity, AnimationData animationData, AnimationRotationData animationRotationData,
                ref LocalTransform rotation) =>
            {
                if (animationData.end) return;
                if (animationData.stop) return;
                rotation.Rotation = Quaternion.Euler(MathTools.Lerp(animationRotationData.start,
                    animationRotationData.end, animationData.value));
            }).Schedule(inputDeps);

        inputDeps = Entities.WithStoreEntityQueryInField(ref scaleGroup).ForEach(
            (Entity entity, AnimationData animationData, AnimationScaleData animationScaleData,
                ref LocalTransform scale) =>
            {
                if (animationData.end) return;
                if (animationData.stop) return;
                var f = MathTools.Lerp(animationScaleData.start, animationScaleData.end, animationData.value);
                scale.Scale = f.x;
            }).Schedule(inputDeps);
        /*inputDeps = Entities.WithStoreEntityQueryInField(ref group).ForEach((int entityInQueryIndex, Entity entity, ref AnimationData animationData) =>
        {
            if (animationData.end)
            {
                commandBuffer.RemoveComponent<AnimationData>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent<AnimationTranslationData>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent<AnimationRotationData>(entityInQueryIndex, entity);
                commandBuffer.RemoveComponent<AnimationScaleData>(entityInQueryIndex, entity);
            }
        }).Schedule(inputDeps);*/

        inputDeps.Complete();
    }
}